{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "measured-option",
   "metadata": {},
   "outputs": [],
   "source": [
    "#!pip install deepface\n",
    "from deepface.commons import functions\n",
    "from deepface import DeepFace\n",
    "import tqdm\n",
    "import os\n",
    "from neo4j import GraphDatabase, basic_auth"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "speaking-delay",
   "metadata": {},
   "source": [
    "# Retrieve facial database"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "pursuant-eligibility",
   "metadata": {},
   "outputs": [],
   "source": [
    "img_paths = []\n",
    "for root, dirs, files in os.walk(\"deepface/tests/dataset/\"):\n",
    "    for file in files:\n",
    "        if '.jpg' in file:\n",
    "            img_path = root+file\n",
    "            img_paths.append(img_path)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "center-stanford",
   "metadata": {},
   "source": [
    "# Face recognition model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "persistent-circus",
   "metadata": {},
   "outputs": [],
   "source": [
    "model = DeepFace.build_model(\"Facenet\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "sensitive-findings",
   "metadata": {},
   "source": [
    "# Find vector representations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "printable-worst",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 60/60 [00:11<00:00,  5.24it/s]\n"
     ]
    }
   ],
   "source": [
    "instances = {}\n",
    "#for img_path in img_paths:\n",
    "for i in tqdm.tqdm(range(0, len(img_paths))):\n",
    "    \n",
    "    img_path = img_paths[i]\n",
    "    \n",
    "    #detect and align\n",
    "    img = functions.preprocess_face(img_path, target_size = (160, 160))\n",
    "    \n",
    "    #represent\n",
    "    embeding = model.predict(img)[0].tolist()\n",
    "    \n",
    "    #raw image name without path and extension\n",
    "    label = img_path.split(\"/\")[-1].split(\".\")[0]\n",
    "    \n",
    "    instances[label] = embeding"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "junior-stretch",
   "metadata": {},
   "source": [
    "# Connect Neo4j"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "advised-faith",
   "metadata": {},
   "outputs": [],
   "source": [
    "driver = GraphDatabase.driver(\"bolt://localhost:7687\", auth=basic_auth(\"neo4j\", \"neo4j\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "dress-rocket",
   "metadata": {},
   "outputs": [],
   "source": [
    "session = driver.session()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "deadly-victor",
   "metadata": {},
   "outputs": [],
   "source": [
    "#flush database\n",
    "result = session.run(\"MATCH (n) DETACH DELETE n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "european-steal",
   "metadata": {},
   "source": [
    "# Creating nodes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "bronze-surrey",
   "metadata": {},
   "outputs": [],
   "source": [
    "statements = \"\"\n",
    "for img_label in instances.keys():\n",
    "    statement = \"MERGE (%s_face:Face {name:'%s'})\" \\\n",
    "                  \" SET %s_face.embedding = %s \\n\" % (img_label, img_label, img_label, instances[img_label])\n",
    "    statements += statement\n",
    "    \n",
    "    \"\"\"if img_label == 'img1':\n",
    "        print(statement)\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "julian-catalyst",
   "metadata": {},
   "outputs": [],
   "source": [
    "with session.begin_transaction() as trx:\n",
    "    trx.run(statements)\n",
    "    trx.commit()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "valued-cabin",
   "metadata": {},
   "source": [
    "# Find distances between nodes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "olympic-thinking",
   "metadata": {},
   "outputs": [],
   "source": [
    "query = \"\"\"\n",
    "    MATCH (p1:Face)\n",
    "    MATCH (p2:Face)\n",
    "    WHERE p1.name <> p2.name\n",
    "    RETURN p1.name AS from, p2.name AS to\n",
    "    , gds.alpha.similarity.euclideanDistance(p1.embedding, p2.embedding) as distance\n",
    "    ORDER BY distance ASC\n",
    "    LIMIT 10\n",
    "\"\"\"\n",
    "\n",
    "results = session.run(query)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "recorded-encoding",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<Record from='img51' to='img57' distance=2.43656215816999>\n",
      "<Record from='img57' to='img51' distance=2.43656215816999>\n",
      "<Record from='img67' to='img51' distance=2.5293301871888247>\n",
      "<Record from='img51' to='img67' distance=2.5293301871888247>\n",
      "<Record from='img67' to='img57' distance=2.6380299746019933>\n",
      "<Record from='img57' to='img67' distance=2.6380299746019933>\n",
      "<Record from='img57' to='img5' distance=2.9911645529701962>\n",
      "<Record from='img5' to='img57' distance=2.9911645529701962>\n",
      "<Record from='img51' to='img5' distance=3.3633656553453206>\n",
      "<Record from='img5' to='img51' distance=3.3633656553453206>\n"
     ]
    }
   ],
   "source": [
    "for result in results:\n",
    "    print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "scheduled-portrait",
   "metadata": {},
   "source": [
    "# Create edges"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "dirty-tragedy",
   "metadata": {},
   "outputs": [],
   "source": [
    "create_edge_statement = \"\"\"\n",
    "    MATCH (p1:Face)\n",
    "    MATCH (p2:Face)\n",
    "    WHERE p1.name <> p2.name\n",
    "    WITH p1, p2, gds.alpha.similarity.euclideanDistance(p1.embedding, p2.embedding) as distance\n",
    "    WHERE distance < 10\n",
    "    MERGE (p1)-[e:distance]-(p2)\n",
    "    SET e.distance=distance\n",
    "\"\"\"\n",
    "\n",
    "results = session.run(create_edge_statement)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "framed-closer",
   "metadata": {},
   "outputs": [],
   "source": [
    "session.close()\n",
    "driver.close()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
